/*This example reads standard from input and writesto the default PCM device for 5 seconds of data.*/
/* Use the newer ALSA API */
#define ALSA_PCM_NEW_HW_PARAMS_API
#include <alsa/asoundlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <time.h>
#include <string.h>
#include <stdio.h>
#include<pthread.h>
#include <semaphore.h>       //sem_t

#define MULTICAST_ENABLED 0
#define HELLO_PORT 12345
#define HELLO_GROUP "225.0.0.37"
#define MSGBUFSIZE 256

pthread_t pthread_t_recv_data;
pthread_t thread_t_play_audio;

#define  CHANNEL 2    /*语音通道*/
#define  ABUFFSIZE 128//(16/8*CHANNEL*32)
#define  BUFFERCOUNT 100000

struct AudioNode
{
   char Buffer[ABUFFSIZE];  //音频缓冲区
   struct AudioNode *next;
};

static int bufcont=0;
static struct AudioNode AudioBuffer[BUFFERCOUNT],*pAudioWrite,*pAudioRead;
static char Playsign=0;

//char *buffer; 		
int size; 		
int rc; 
snd_pcm_t *handle; 		
snd_pcm_uframes_t frames; 		
int fd;
struct sockaddr_in addr;
pthread_mutex_t audio_rec_lock;
sem_t audioplaysem;
     
extern void init_alsa_playback();//初始化播放器
extern void free_alsa_playback();//释放播放器资源
extern void init_udp_communication();//初始化udp通讯

void thread_recv_data(void *arg)
{

		int nbytes;
		int addrlen=sizeof(addr);
				printf("000000000000\n");		
		while(1)
		{		
		   //while( Playsign==0)
   			//usleep(1000);
   			
   			//while(bufcont>1000)
   				//usleep(1);
   				
				//pthread_mutex_lock(&audio_rec_lock);
				//printf("%s: 11111111\n", __FUNCTION__);
				if ((nbytes=recvfrom(fd, pAudioWrite->Buffer, ABUFFSIZE,0,
								 (struct sockaddr *) &addr,&addrlen)) < 0) 
				{
						 perror("recvfrom");
						 exit(1);
				}
				//printf("%s: 222222222\n", __FUNCTION__);

				pAudioWrite = pAudioWrite->next; //写入缓冲区
				pthread_mutex_lock(&audio_rec_lock);		
				bufcont++;
				pthread_mutex_unlock(&audio_rec_lock);
				
          				//sem_post(&audioplaysem);
          								
				//printf("%s: bufcont=%d\n", __FUNCTION__, bufcont);		
				if(/*(Playsign==0)&&*/(bufcont>5))
				{  
					Playsign=1;
				   //sem_post(&sem_Synchronous); //网络已经准备好开始接收数据
				}

				//pthread_mutex_unlock(&audio_rec_lock);
    }		  
}

static int xrun_recovery(snd_pcm_t *handle, int err)
{
				int verbose = 1;
				//int err=0;
        if (verbose)
                printf("stream recovery\n");
        if (err == -EPIPE) {    /* under-run */
                err = snd_pcm_prepare(handle);
                if (err < 0)
                        printf("Can't recovery from underrun, prepare failed: %s\n", snd_strerror(err));
                return 0;
        } else if (err == -ESTRPIPE) {
                while ((err = snd_pcm_resume(handle)) == -EAGAIN)
                        sleep(1);       /* wait until the suspend flag is released */
                if (err < 0) {
                        err = snd_pcm_prepare(handle);
                        if (err < 0)
                                printf("Can't recovery from suspend, prepare failed: %s\n", snd_strerror(err));
                }
                return 0;
        }
        return err;
}

void thread_play_audio(void *arg)
{
			int err;
							printf("%s: aaaaaaaaaaaaaaa\n", __FUNCTION__);						
			while(1)
			{
							//printf("%s: bbbbbbbb\n", __FUNCTION__);						

							//printf("%s: cccccccccccc\n", __FUNCTION__);						
					if((bufcont>0)&&(Playsign==1))
					{
     				//sem_wait(&audioplaysem);
							//printf("%s: dddddddddddddd\n", __FUNCTION__);						

							//printf("收到%d字节, size=%d\n", nbytes, size);

							rc = snd_pcm_writei(handle, pAudioRead->Buffer, 32);   
							//printf("%s: fffffffffffffffff,rc=%d\n", __FUNCTION__, rc);						
							if (rc == -EPIPE) 
							{    
								/* EPIPE means underrun */  
								 fprintf(stderr, "underrun occurred\n");   
								 snd_pcm_prepare(handle); 
							}
							else if (rc < 0) 
							{     
									//fprintf(stderr, "error from writei: %s\n",   snd_strerror(rc));  
									err = xrun_recovery(handle, err);
									if (err < 0) {
										 printf("Write error: %s\n", snd_strerror(err));
										 exit(1);
									}									
							 } 
							else if (rc != (int)32) 
							{    
							 		fprintf(stderr, "short write, write %d frames\n", rc); 
							}
							else
							{
									pAudioRead = pAudioRead->next;
									pthread_mutex_lock(&audio_rec_lock);
									bufcont--;			//读取音频
									pthread_mutex_unlock(&audio_rec_lock);						
									printf("%s: bufcont=%d\n", __FUNCTION__, bufcont);		
							}
							#if 0
							while(bufcont>5)
							{
									pAudioRead = pAudioRead->next;
									bufcont--;			//读取音频
							}	
							#endif


				 }

			}
}

/****************************************************************************************
*   函数名: audioInitbuf
*   输  入: 无
*   输  出: 无
*   功能说明：音频播放缓冲区初始化
*
*       
******************************************************************************************/
void audioInitbuf(void)
{
       int i;                                                            /*音频播放缓冲区初始化*/
       for(i=0;i<(BUFFERCOUNT-1);i++)                                     /*播放缓冲区连成一个环形缓冲区*/
       {
         AudioBuffer[i].next=&AudioBuffer[i+1];
       }
       
       AudioBuffer[BUFFERCOUNT-1].next=&AudioBuffer[0];                                /*最后一个链接*/
       pAudioWrite=&AudioBuffer[0];                                                  /*读写指针初始化*/
       pAudioRead=&AudioBuffer[0];   
       bufcont=0;                                                           /*采集到的数据计算*/          
} 
							
int main() 
{ 
		long loops;      
		int  nbytes,addrlen;
		char msgbuf[MSGBUFSIZE];
   pthread_attr_t tattr;

    pthread_mutex_init (&audio_rec_lock, NULL);
        sem_init(&audioplaysem,0,0);
		init_alsa_playback();//初始化播放器
		init_udp_communication();

		int err;

		addrlen=sizeof(addr);
		
		audioInitbuf();

   int ret = pthread_attr_init(&tattr); //读取线程属性
   ret = pthread_attr_setdetachstate(&tattr,PTHREAD_CREATE_DETACHED);                       //线程为分离线程
   ret = pthread_attr_setguardsize(&tattr, 0);
   ret = pthread_attr_setstacksize(&tattr, 128*1024); //堆栈大小设定
   		
		printf("%s: 创建数据接收线程\n", __FUNCTION__);
		err = pthread_create(&pthread_t_recv_data, &tattr, (void*)thread_recv_data, NULL);
		printf("err = %d\n", err);
		if(err != 0)
		{
				printf("can't create thread: %s\n",strerror(err));
				return 1;
		}

		printf("%s: 创建语音播放线程\n", __FUNCTION__);
		err = pthread_create(&thread_t_play_audio, NULL, (void*)thread_play_audio, NULL);
		printf("err = %d\n", err);		
		if(err != 0)
		{
				printf("can't create thread: %s\n",strerror(err));
				return 1;
		}  
		
		pthread_join (pthread_t_recv_data, NULL);
		pthread_join (thread_t_play_audio, NULL);		
		
		//free_alsa_playback();
		return 0;		
}

void init_alsa_playback()
{
		snd_pcm_hw_params_t *params; 
		unsigned int val; 
		int dir; 

				
		/* Open PCM device for playback. */
		rc=snd_pcm_open(&handle,"default", SND_PCM_STREAM_PLAYBACK, 0);
		if (rc< 0) 
		{ 
			fprintf(stderr,  "unable to open pcm device: %s\n",    snd_strerror(rc));
			exit(1); 
		} 
		 
		/* Allocate a hardware parameters object. */
		 snd_pcm_hw_params_alloca(&params); 
		/* Fill it in with default values. */
		 snd_pcm_hw_params_any(handle, params);
		 /* Set the desired hardware parameters. */ 
		/* Interleaved mode */ 
		snd_pcm_hw_params_set_access(handle,params, SND_PCM_ACCESS_RW_INTERLEAVED); 
		/* Signed 16-bit little-endian format */ 
		snd_pcm_hw_params_set_format(handle,params, SND_PCM_FORMAT_S16_LE); 
		/* Two channels (stereo) */  snd_pcm_hw_params_set_channels(handle, params, 2); 
		/* 44100 bits/second sampling rate (CD quality) */ 
		val = 44100; 
		snd_pcm_hw_params_set_rate_near(handle,params, &val, &dir);  /* Set period size to 32 frames. */ 
		frames = 32; 
		snd_pcm_hw_params_set_period_size_near(handle, params, &frames, &dir);
		/* Write the parameters to the driver */
		 rc = snd_pcm_hw_params(handle, params);
		 if (rc < 0) 
		 {   
				fprintf(stderr, "unable to set hw parameters: %s\n", snd_strerror(rc));   
				exit(1); 
		}

		/*Use a buffer large enough to hold one period*/
		snd_pcm_hw_params_get_period_size(params, &frames,  &dir); //32个采样点算一帧
		size = frames * 4;
		/* 2 bytes/sample, 2 channels */ 
		//buffer = (char *) malloc(size); 
		/* We want to loop for 5 seconds */
		snd_pcm_hw_params_get_period_time(params, &val, &dir); 
}

void free_alsa_playback()
{
		snd_pcm_drain(handle);
		snd_pcm_close(handle);
		//free(buffer); 
}

void init_udp_communication()
{
     struct ip_mreq mreq;
     
     //允许接收组播信息
     #if defined(MULTICAST_ENABLED) && (MULTICAST_ENABLED==1)
     u_int yes=1;            /*** MODIFICATION TO ORIGINAL */
		 #endif
		 
		//创建UDP SOCKET
     if ((fd=socket(AF_INET,SOCK_DGRAM,0)) < 0) 
     {
				perror("socket");
				exit(1);
     }


     #if defined(MULTICAST_ENABLED) && (MULTICAST_ENABLED==1)
    /* allow multiple sockets to use the same PORT number */
    if (setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(yes)) < 0) 
    {
       perror("Reusing ADDR failed");
       exit(1);
       }
		#endif
		
     /* set up destination address */
     memset(&addr,0,sizeof(addr));
     addr.sin_family=AF_INET;
     addr.sin_addr.s_addr=htonl(INADDR_ANY); /* N.B.: differs from sender */
     addr.sin_port=htons(HELLO_PORT);
     
			/* bind to receive address */
			if (bind(fd,(struct sockaddr *) &addr,sizeof(addr)) < 0) 
			{
					perror("bind");
					exit(1);
			}
     
			#if defined(MULTICAST_ENABLED) && (MULTICAST_ENABLED==1)
			/* use setsockopt() to request that the kernel join a multicast group */
			mreq.imr_multiaddr.s_addr=inet_addr(HELLO_GROUP);
			mreq.imr_interface.s_addr=htonl(INADDR_ANY);
			if (setsockopt(fd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq,sizeof(mreq)) < 0) 
			{
					perror("setsockopt");
					exit(1);
			}
			#endif
}


